﻿using Mayn.Record;
using Minio;
using Newtonsoft.Json;
using Sunny.UI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Mayn.Video
{
    public partial class FrmCamera : UIForm
    {
        #region 录屏全局变量
        CameraRecorder recorder = null;
        CameraRecorder recorder2 = null;

        Stopwatch sw1 = new Stopwatch();
        Stopwatch sw2 = new Stopwatch();
        private bool running = false;
        private int cameraCount = 0;

        private List<UIComboBox> cmbList = new List<UIComboBox>();
        private string ipAddress = "";
        string fileName1, fileName2;

        Form prevForm1 = new Form();
        PictureBox prevPic1 = new PictureBox();

        Form prevForm2 = new Form();
        PictureBox prevPic2 = new PictureBox();

        private int fps1 = 60;
        private int fps2 = 57;

        private int CameraFpsIndex = 0;
        private int CardFpsIndex = 0;

        #endregion

        #region 数据访问
        private WmsVideoScan videoScan = null;
        private WmsVideoScanDal videoScanDal = new WmsVideoScanDal();
        private SysLogDal logDal = new SysLogDal();
        #endregion

        private GlobalKeyboardHook hook;
        private string saveDisk;
        private string videoPath;
        private string zipPath;
        private string videoDateFilder = DateTime.Now.ToString("MMddHHmmss");

        public FrmCamera()
        {
            InitializeComponent();
            InitializeData();
            DeleteFile();
            InitializeChildComponent();
        }

        private void InitializeData()
        {
            hook = new GlobalKeyboardHook();
            hook.EndKeyPressed += OnEndKeyPressed;
            saveDisk = Common.appSettingVale("SavePath");
            videoPath = saveDisk + "MaynVideo\\" + Common.appSettingVale("VideoPath") + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\";
            zipPath = saveDisk + "MaynVideo\\" + Common.appSettingVale("ZipPath") + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\";

            CameraFpsIndex = Convert.ToInt32(Common.appSettingVale("CameraFpsIndex"));
            CardFpsIndex = Convert.ToInt32(Common.appSettingVale("CardFpsIndex"));

        }

        public void DeleteFile()
        {
            string[] subDirectories = Directory.GetDirectories(saveDisk + "MaynVideo\\VideoPath");
            foreach (var sub in subDirectories)
            {
                if (Convert.ToDateTime(Path.GetFileName(sub)) < DateTime.Now.Date)
                {
                    Directory.Delete(sub, true);
                }
            }
        }


        public void InitializeChildComponent()
        {
            prevForm1.Text = "";
            prevForm1.Size = new System.Drawing.Size(1350, 780);
            prevForm1.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.prevForm1_FormClosing);
            prevForm1.MaximizeBox = false;
            prevForm1.MinimizeBox = false;
            prevForm1.ShowIcon = false;
            prevForm1.FormBorderStyle = FormBorderStyle.FixedSingle;

            prevPic1.Dock = System.Windows.Forms.DockStyle.Fill;
            prevPic1.Size = new System.Drawing.Size(1280, 720);
            prevPic1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
            prevForm1.Controls.Add(prevPic1);

            prevForm2.Text = "";
            prevForm2.Size = new System.Drawing.Size(1350, 780);
            prevForm2.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.prevForm2_FormClosing);
            prevForm2.MaximizeBox = false;
            prevForm2.MinimizeBox = false;
            prevForm2.ShowIcon = false;
            prevForm2.FormBorderStyle = FormBorderStyle.FixedSingle;

            prevPic2.Dock = System.Windows.Forms.DockStyle.Fill;
            prevPic2.Size = new System.Drawing.Size(1280, 720);
            prevPic2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
            prevForm2.Controls.Add(prevPic2);
        }

        private void FrmCamera_Load(object sender, EventArgs e)
        {
            hook.InstallHook();
            loadCameraList();
            updateAppPostion();
            openCamera();
            ipAddress = Common.ip4Address();
            timer2.Start();
        }

        private void prevForm1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                prevForm1.Hide();
                e.Cancel = true;
            }
        }

        private void prevForm2_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                prevForm2.Hide();
                e.Cancel = true;
            }
        }

        /// <summary>
        /// 右上顶部打开
        /// </summary>
        private void updateAppPostion()
        {
            //设置当前窗体右上显示
            this.Top = 0;
            this.Left = Screen.PrimaryScreen.Bounds.Width - this.Width;
            this.Height = Screen.PrimaryScreen.Bounds.Height;

            //设置自适应高度
            this.conTop.Height = (this.Height - this.TitleHeight) / 2;
            this.conBot.Top = this.conTop.Height + this.TitleHeight;
            this.conBot.Height = this.conTop.Height;

            this.picBox1.Height = this.conTop.Height - this.uiPanel1.Height - btnStart1.Height - 35;
            this.picBox2.Height = this.conBot.Height - this.uiPanel2.Height - btnStart2.Height - 35;

            Process[] processes = Process.GetProcesses();
            List<Process> desktopApps = new List<Process>();

            // 设置窗体大小和位置
            int screenWidth = Screen.PrimaryScreen.Bounds.Width;
            int screenHeight = Screen.PrimaryScreen.Bounds.Height;
            foreach (Process process in processes)
            {
                if (!string.IsNullOrEmpty(process.MainWindowTitle) && process.MainWindowHandle != IntPtr.Zero && process.MainWindowHandle != GetShellWindow())
                {
                    desktopApps.Add(process);
                    IntPtr mainWindowHandle = process.MainWindowHandle;
                    RECT rect;
                    if (GetWindowRect(mainWindowHandle, out rect))
                    {
                        int width = rect.right - rect.left - this.Width;
                        int height = rect.bottom - rect.top;
                        SetWindowPos(mainWindowHandle, new IntPtr(1), 0, 0, width, height, 0x0002 | 0x0040);
                    }
                }
            }
        }

        /// <summary>
        /// 获取摄像头
        /// </summary>
        private void loadCameraList()
        {
            recorder = new CameraRecorder();
            recorder.NewFrame = frame =>
            {
                picBox1.Image = (Bitmap)frame.Clone();

            };

            recorder2 = new CameraRecorder();
            recorder2.NewFrame = frame =>
            {
                picBox2.Image = (Bitmap)frame.Clone();

            };

            var devs = recorder.Init();
            this.cmbDevice1.Items.Clear();
            this.cmbDevice2.Items.Clear();
            if (devs.Any())
            {
                this.cmbDevice1.Items.AddRange(devs.ToArray());
                this.cmbDevice1.SelectedItem = devs[0];
                if (devs.Count > 1)
                {
                    recorder2.Init();
                    this.cmbDevice2.Items.AddRange(devs.ToArray());
                    this.cmbDevice2.SelectedItem = devs[1];
                    cameraCount = devs.Count;
                }
            }
        }


        /// <summary>
        /// 摄像头画面断开
        /// </summary>
        private void disconnectedImage()
        {
            picBox1.ImageLocation = picBox2.ImageLocation = @"Images\unconn.jpg";
        }


        /// <summary>
        /// 开始录制
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart1_Click(object sender, EventArgs e)
        {
            //startVideo(false);
        }

        /// <summary>
        /// 开始录制
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart2_Click(object sender, EventArgs e)
        {
            //startVideo();
        }

        /// <summary>
        /// 停止录制
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnEnd1_Click(object sender, EventArgs e)
        {
            stopVideo();
        }

        /// <summary>
        /// 停止录制
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnEnd2_Click(object sender, EventArgs e)
        {
            stopVideo();
        }

        /// <summary>
        /// 视频预览
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReplay1_Click(object sender, EventArgs e)
        {
            reviewVideo(fileName1);
        }


        private void FrmCamera_FormClosing(object sender, FormClosingEventArgs e)
        {
            hook.UninstallHook();
            closeCamera();
            stopVideo();
        }

        private void closeCamera()
        {
            recorder.CloseCamera();
            recorder2.CloseCamera();
        }

        /// <summary>
        /// End键按下结束录像
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnEndKeyPressed(object sender, EventArgs e)
        {
            stopVideo();
        }

        /// <summary>
        /// 在未开始前从共享内存读取数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer2_Tick(object sender, EventArgs e)
        {
            string msg = MemoryShare.ReadMsg();
            if (!string.IsNullOrEmpty(msg))
            {
                VideoScanModel scanInfo = JsonConvert.DeserializeObject<VideoScanModel>(msg);
                IList<WmsVideoScan> existList = videoScanDal.GetAll(t => t.ORDER_NO == scanInfo.so && t.TYPE == scanInfo.type && t.DEL == 0);
                foreach (WmsVideoScan vs in existList)
                {
                    videoScanDal.updateDel(vs);
                }
                //重新添加数据
                videoScan = videoScanDal.InsertScan(scanInfo.so, ipAddress, scanInfo.type, scanInfo.creator);

                timer2.Stop();
                startVideo(true);
            }
        }

        /// <summary>
        /// 视频预览
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnReplay2_Click(object sender, EventArgs e)
        {
            reviewVideo(fileName2);
        }

        /// <summary>
        /// 视频预览方法
        /// </summary>
        /// <param name="videoPath"></param>
        private void reviewVideo(string videoPath)
        {
            try
            {
                Common.reviewVideo(videoPath);
            }
            catch (Exception ex)
            {
                ShowErrorTip($"请确保已安装视频播放器并设为默认打开!");
                return;
            }
        }

        /// <summary>
        /// 开始录制
        /// </summary>
        private void startVideo(bool auto)
        {
            updateButtonsStatus(false);
            StartRecorder(auto);
        }
        private void StartRecorder(bool auto)
        {
            if (auto)
            {
                fileName1 = videoPath + generateFileName("A");
                fileName2 = videoPath + generateFileName("B");
            }
            else
            {
                fileName1 = Path.Combine(videoPath, videoDateFilder + "_1.avi");
                fileName2 = Path.Combine(videoPath, videoDateFilder + "_2.avi");
            }

            recorder.StartRecorder(fileName1, fps2);
            if (cameraCount > 1)
            {
                recorder2.StartRecorder(fileName2, fps1);
            }
            sw1.Restart();
            timer1.Start();

            running = true;
        }

        private void openCamera()
        {
            long diskSapce = Common.getHardDiskFreeSpace(saveDisk);
            int minSpace = Convert.ToInt32(Common.appSettingVale("MinSpace"));
            if (diskSapce < minSpace)
            {
                ShowErrorDialog($"视频保存路径为D盘，请保证D盘空间不能小于{minSpace}G!",true);
                Application.Exit();
                return;
            }
            if (!Directory.Exists(videoPath))
            {
                Directory.CreateDirectory(videoPath);
            }

            if (cameraCount > 1)
            {
                if (cmbDevice2.SelectedItem.ToString().Contains("4K"))
                {
                    fps2 = 30;
                    CardFpsIndex = Convert.ToInt32(Common.appSettingVale("CameraFpsIndex"));
                }
                //recorder2.Open(1, CardFpsIndex);
            }
            if (cmbDevice1.SelectedItem.ToString().Contains("4K"))
            {
                fps1 = 30;
                CameraFpsIndex = Convert.ToInt32(Common.appSettingVale("CameraFpsIndex"));
            }
            //recorder.Open(0, CameraFpsIndex);

            recorder2.Open(0, CameraFpsIndex);
            recorder.Open(1, CardFpsIndex);

            //if (cameraCount > 1)
            //{
            //    if (cmbDevice2.SelectedItem.ToString().Contains("4K"))
            //    {
            //        fps2 = 30;
            //        CardFpsIndex = Convert.ToInt32(Common.appSettingVale("CameraFpsIndex"));
            //    }
            //    //recorder2.Open(1, CardFpsIndex);
            //    recorder2.Open(0, CardFpsIndex);
            //}



        }

        /// <summary>
        /// 生成文件名称
        /// </summary>
        /// <returns></returns>
        private string generateFileName(string suffix)
        {
            string fileName = $"{videoScan.ORDER_NO}_{videoScan.TYPE}_{suffix}.mp4";
            return fileName;
        }


        /// <summary>
        /// 更新按钮状态
        /// </summary>
        /// <param name="enabled">是否可用</param>
        private void updateButtonsStatus(bool enabled)
        {
            btnStart1.Enabled = btnReplay1.Enabled = cmbDevice1.Enabled = cmbRES1.Enabled == enabled;
            lbStatus1.Text = enabled ? "录像成功" : "录像中...";
            lbStatus1.ForeColor = System.Drawing.Color.FromArgb(67, 156, 0);
            if (enabled)
            {
                cmbDevice1.FillColor = cmbRES1.FillColor = System.Drawing.Color.FromArgb(47, 57, 84);
            }
            if (cameraCount > 1)
            {
                btnStart2.Enabled = btnReplay2.Enabled = cmbDevice2.Enabled = cmbRES2.Enabled = enabled;
                lbStatus2.Text = enabled ? "录像成功" : "录像中...";
                lbStatus2.ForeColor = System.Drawing.Color.FromArgb(67, 156, 0);
                if (enabled)
                {
                    cmbDevice2.FillColor = cmbRES2.FillColor = System.Drawing.Color.FromArgb(47, 57, 84);
                }
            }
        }

        private void picBox1_Click(object sender, EventArgs e)
        {
            prevPic1.Image = picBox1.Image;
            prevForm1.Show();
            prevForm2.Hide();
        }

        private void picBox2_Click(object sender, EventArgs e)
        {
            prevPic2.Image = picBox2.Image;
            prevForm2.Show();
            prevForm1.Hide();
        }


        /// <summary>
        /// 结束录制
        /// </summary>
        private void stopVideo()
        {
            if (running)
            {
                running = false;
                string filePath = fileName1;

                recorder.StopRecorder();
                if (cameraCount > 1)
                {
                    recorder2.StopRecorder();
                    filePath += "," + fileName2;
                }
                videoScanDal.videoEnd(videoScan, sw1.Elapsed.TotalSeconds, filePath);
                updateButtonsStatus(true);
                sw1.Stop();
                timer2.Start();
                uploadFile(videoScan.ORDER_NO, videoScan.TYPE);
            }
        }

        /// <summary>
        /// 更新视频录制时间
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            lbTime1.Text = lbTime2.Text = sw1.Elapsed.Hours.ToString("00") + ":" + sw1.Elapsed.Minutes.ToString("00") + ":" + sw1.Elapsed.Seconds.ToString("00");
        }


        private void uploadFile(string orderNo, string type)
        {
            Task.Run(() =>
            {
                uploadZipFile(orderNo, type);
            });
        }

        private void zipLocalFile()
        {
            Stopwatch sw = new Stopwatch();

            sw.Restart();
            string result = excuteZip(fileName1);
            sw.Stop();
            logDal.WriteSysLog(nameof(FrmCamera), this.Name, Path.GetFileName(fileName1), sw.Elapsed.TotalSeconds.ToString(), "压缩时间", "");

            if (cameraCount > 1)
            {
                sw.Restart();
                result = excuteZip(fileName2);
                sw.Stop();
                logDal.WriteSysLog(nameof(FrmCamera), this.Name, Path.GetFileName(fileName1), sw.Elapsed.TotalSeconds.ToString(), "压缩时间", "");
            }

            if (!string.IsNullOrEmpty(result))
            {
                videoScan.STATUS = (int)VidoStatusEnum.Zip;
                videoScanDal.Update(videoScan);
            }
        }

        /// <summary>
        /// 执行压缩
        /// </summary>
        /// <param name="filePath">要压缩文件路径</param>
        /// <returns></returns>
        private string excuteZip(string filePath)
        {
            try
            {
                string outputPath = zipPath + Path.GetFileName(filePath);
                string ffmpegPath = saveDisk + "MaynVideo\\" + "ffmpeg.exe"; // FFmpeg可执行文件路径
                int crf = 35; //压缩质量 0-51（值越小，质量越高，但文件大小也越大）,
                int threads = 3; //开启压缩的线程数量
                string preset = "veryfast";  // ultrafast(最快)、superfast(超级快)、veryfast(非常快)、faster(稍微快)、fast(快速)、medium（折中）

                // 创建一个进程对象
                Process process = new Process();
                process.StartInfo.FileName = ffmpegPath;
                process.StartInfo.Arguments = $"-i {filePath} -vcodec libx264 -crf {crf} -threads {threads} -preset {preset} {outputPath}";
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.RedirectStandardError = true;
                process.Start();
                process.BeginErrorReadLine();

                // 等待进程结束
                process.WaitForExit();
                if (!process.HasExited)
                {
                    process.Kill(); // 手动终止进程
                }
                return outputPath;
            }
            catch (Exception ex)
            {
                return "";
            }
        }

        /// <summary>
        /// 上传压缩后的文件
        /// </summary>
        /// <param name="orderNo"></param>
        /// <param name="type"></param>
        private async void uploadZipFile(string orderNo, string type)
        {
            try
            {
                await Task.Delay(1000);
                string fileName = "";
                PutObjectResponse res;

                Stopwatch sw = new Stopwatch();
                sw.Restart();

                //string zipPath1 = zipPath + Path.GetFileName(fileName1);
                string zipPath1 = videoPath + Path.GetFileName(fileName1);
                if (File.Exists(zipPath1))
                {
                    res = await MinioHelper.FPutObject(videoDateFilder, Path.GetFileName(fileName1), zipPath1);
                }

                sw.Stop();
                logDal.WriteSysLog(nameof(FrmCamera), this.Name, Path.GetFileName(fileName1), sw.Elapsed.TotalSeconds.ToString(), "Minio上传时间", "");

                fileName = Path.GetFileName(fileName1);
                if (cameraCount > 1)
                {
                    sw.Restart();
                    //string zipPath2 = zipPath + Path.GetFileName(fileName2);
                    string zipPath2 = videoPath + Path.GetFileName(fileName2);
                    if (File.Exists(zipPath2))
                    {
                        res = await MinioHelper.FPutObject(videoDateFilder, Path.GetFileName(fileName2), zipPath2);
                    }
                    sw.Stop();
                    logDal.WriteSysLog(nameof(FrmCamera), this.Name, Path.GetFileName(fileName2), sw.Elapsed.TotalSeconds.ToString(), "Minio上传时间", "");
                    fileName += "," + Path.GetFileName(fileName2);
                }

                videoScan.STATUS = (int)VidoStatusEnum.UploadLocal;
                videoScan.VIDEO_NAME = fileName;
                videoScanDal.Update(videoScan);

            }
            catch (Exception ex)
            {
                logDal.WriteSysLog(nameof(FrmCamera), this.Name, Path.GetFileName(fileName1), "上传失败!" + ex.Message, "", "");
            }
        }

        #region Windows API
        [DllImport("user32.dll")]
        static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        // 获取Windows桌面窗口句柄
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr GetShellWindow();

        [DllImport("user32.dll")]
        static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [StructLayout(LayoutKind.Sequential)]
        struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }
        #endregion
    }
}
